package com.net.xpay.web.secutiry;

import com.google.common.base.Strings;
import com.net.common.util.SerializationUtil;
import com.net.common.util.ServletUtil;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.UserProfile;
import com.net.xpay.common.enums.config.CommonSysConfigKey;
import com.net.xpay.common.helper.CommonSysConfigHelper;
import com.net.xpay.common.manager.UserManager;
import com.net.xpay.common.manager.UserProfileManager;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.constant.WebConstant;
import com.net.xpay.core.helper.DomainHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;

/**
  on 01/02/2018.
 */
@Slf4j
@Component
public class AccessDecisionManagerImpl implements AccessDecisionManager {
    @Autowired
    private UserManager userManager;
    @Autowired
    private SecurityHelper securityHelper;
    @Autowired
    private SessionManager sessionManager;
    @Autowired
    private DomainHelper domainHelper;
    @Autowired
    private CommonSysConfigHelper commonSysConfigHelper;
    @Autowired
    private UserProfileManager userProfileManager;

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        HttpServletRequest request = ((FilterInvocation) object).getRequest();
        HttpServletResponse response = ((FilterInvocation) object).getResponse();

        Operator operator;
        if (authentication instanceof AnonymousAuthenticationToken) {
            operator = null;
        } else {
            operator = (Operator) authentication;
        }
        //删除无效的session cookie
        if (operator == null && request.getRequestedSessionId() != null
                && !request.isRequestedSessionIdValid()) {
            securityHelper.removeInvalidSessionCookie(request, response);
        }
        User user = populateUser(operator, request);//获取用户信息User并放入到session_user_attr, populate the operator,防止数据不一致

        decideByUserType(operator, user, request, response); //不同用户类型的访问路径不一样的
    }

    private User populateUser(Operator operator, HttpServletRequest request) {
        if (operator != null && operator.getUserId() != null) {
            User user = userManager.getById(operator.getUserId());
            if (user == null) {
                log.warn("populateUser user null|userId={}|operator={}|uri={}", operator.getUserId(), SerializationUtil.obj2String(operator), request.getRequestURI());
                return null;
            }
            //黑名单处理
            sessionManager.checkUserSecurity(user);

            //判断是否允许登录商户端
            checkOpenLoginMerchantPlatform(user, request);

            operator.setStatus(OperatorStatus.NORMAL_LOGIN);
            operator.setUserId(user.getId());

            request.setAttribute(WebConstant.REQUEST_USER_ATTR, user);
            return user;
        }
        return null;
    }

    private void checkOpenLoginMerchantPlatform(User user, HttpServletRequest request) {
        UserProfile userProfile = userProfileManager.getById(user.getId());
        if (Boolean.FALSE.equals(userProfile.getOpenMerchantPlatform())) {
            String merchantDomain = commonSysConfigHelper.getString(CommonSysConfigKey.MERCHANT_DOMAIN).trim();
            String domain = domainHelper.getCookieDomain(request).trim();

            if (merchantDomain.equals(domain)) {
                throw new WebInsufficientAuthException(PoseidonErrorCode.NEED_LOGIN, "未入驻商户平台");
            }
        }

    }

    private void decideByUserType(Operator operator, User user, HttpServletRequest request, HttpServletResponse response) {
        String path = ServletUtil.PATH_SEP + ServletUtil.getTopmostPath(request);
        if (Strings.isNullOrEmpty(path)) {
            throw new WebInsufficientAuthException(PoseidonErrorCode.REQUEST_URL_ILLEGAL, "请求链接错误,path为空");
        }
        if (WebConstant.COMMON.equalsIgnoreCase(path) || WebConstant.OPEN.equalsIgnoreCase(path)) {
            return;
        } else if (operator == null) {
            throw new WebInsufficientAuthException(PoseidonErrorCode.NEED_LOGIN, "登录已过期请重新登录");
        } else if (user == null) {
            securityHelper.removeInvalidSessionCookie(request, response);
            log.warn("decideByUserType user null|operator={}|uri={}", SerializationUtil.obj2String(operator), request.getRequestURI());
            throw new WebInsufficientAuthException(PoseidonErrorCode.NEED_LOGIN, "登录已过期 请重新登录");
        }
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}
